#!/bin/sh
# Copyright(c) 2022 https://github.com/WangXuan95
#
# function : a single useful script for running Bluespec SystemVerilog (BSV) simulation, generating simulation wave, or generating Verilog source.
#
# to see usage, run:
#     sh bsvbuild.sh
#

alias echo="echo -e"

# colors for print text ------------------------------------------------------------------------------------------------------------------
error_color="\033[1;31m"                     # echo this to change print text color for Error message. (31 is red)
note_color="\033[1;36m"                      # echo this to change print text color for Important compile note. (36 is light blue) 
runsim_color="\033[1;33m"                    # echo this to change print text color for Simulation run print message. (33 is yellow) 
default_color="\033[m"                       # echo this to change print text color to default color (e.g., white)


# name of the BSV compiler ------------------------------------------------------------------------------------------------------------------
compiler_name="bsc"


# some compile temporary file name (fixed) ------------------------------------------------------------------------------------------------------------------
sim_exe_file="sim.out"
sim_so_file=$sim_exe_file".so"
v_vcd_file_tmp="dump.vcd"                    # This is the temporary .vcd file generated by Verilog simulation (generated by command: ./vsim +bscvcd)


# option variables (from command line arguments) ------------------------------------------------------------------------------------------------------------------
top_module="mkTb"                            # top module          , default is mkTb
top_file="Tb.bsv"                            # top file            , default is Tb.bsv
log_file="/dev/stdout"                       # simulation log file , default is /dev/stdout, i.e., print to screen
time_argument=""                             # time argument for BSV simulation, default is infinite, i.e., simulate until $finish


# usage (help) string ------------------------------------------------------------------------------------------------------------------
usage="\
\n      usage: run following command under the directory which contains .bsv source file(s):          \
\n\t        "$0" -<param> [<top_module>] [<top_file_name>] [<log_file_name>] [<sim_time>]             \
\n                                                                                                    \
\n      arguments:                                                                                    \
\n\t        -<param>:                                                                                 \
\n\t\t          -bs  \t: run BSV simulation.                                                          \
\n\t\t          -bw  \t: generate BSV simulation wave.                                                \
\n\t\t          -bsw \t: run BSV simulation and generate wave.                                        \
\n\t\t          -v   \t: generate Verilog source only.                                                \
\n\t\t          -vs  \t: generate Verilog source and run Verilog simulation.                          \
\n\t\t          -vw  \t: generate Verilog source and generate Verilog simulation wave.                \
\n\t\t          -vsw \t: generate Verilog source, run Verilog simulation and generate wave.           \
\n\t\t          -clean : remove temporary files in current directory using:                           \
\n\t\t\t\t                 rm *.bo *.ba *.cxx *.h *.o "$sim_exe_file" "$sim_so_file"                  \
\n\t        <top_module>:                                                                             \
\n\t\t          Top level module name, default is "$top_module"                                       \
\n\t        <top_file_name>:                                                                          \
\n\t\t          Top level .bsv source file name, default is "$top_file"                               \
\n\t        <log_file_name>:                                                                          \
\n\t\t          Simulation output log file, default is "$log_file" (display on screen)                \
\n\t        <sim_time>:                                                                               \
\n\t\t          Maximum simulation time, default is infinite, i.e., simulate until \$finish           \
\n                                                                                                    \
\n      example:                                                                                      \
\n\t        "$0" -vsw mkCounter Counter.bsv                                                           \
\n                                                                                                    \
\n      dependency:                                                                                   \
\n\t        1. bsc : BSV compiler                                                                     \
\n\t        2. iverilog : Verilog simulator                                                           \
\n                                                                                                    \
\n      The meaning of printing colors:                                                               \
\n\t        "$error_color"1. error message"$default_color"                                            \
\n\t        "$note_color"2. compilation commands and important notes"$default_color"                  \
\n\t        "$runsim_color"3. simulation print, e.g., from \$display() in BSV"$default_color"         \
\n"


# exit if there is no enough arguments ------------------------------------------------------------------------------------------------------------------
if   [ $# -lt 1 ]; then                      # if there are no enough args,
    echo $usage                              #    print usage
    exit                                     #    and exit
fi


# parsing command-line args ------------------------------------------------------------------------------------------------------------------
for arg in $*
do
    if   [ $arg = $1 ]; then                                           # if it is the 1st arg
        param=$arg
    elif [ ${arg##*.}x = "bsv"x ]; then                                # if arg ends with ".bsv"
        top_file=${arg##*/}
    elif [ ${arg##*.}x = "txt"x ] || [ ${arg##*.}x = "log"x ]; then    # if arg ends with ".txt" or ".log"
        log_file=$arg
    elif [ "$arg" -gt 0 ] 2>/dev/null; then                            # if arg is a number
        time_argument="-m "$arg
    else                                                               # else
        top_module=$arg
    fi
done


# ------------------------------------------------------------------------------------------------------------------
if [ $param = '-bw' ] || [ $param = '-vw' ]; then
    log_file="/dev/null"
fi


# set generated wave file name (.vcd) ------------------------------------------------------------------------------------------------------------------
b_vcd_file=$top_module"_bw.vcd"          # e.g., if top_module is mkTb, then generate BSV     simulation wave file name is "mkTb_bw.vcd"
v_vcd_file=$top_module"_vw.vcd"          # e.g., if top_module is mkTb, then generate Verilog simulation wave file name is "mkTb_vw.vcd"


# if param=="-clean", remove compile temporary files and exit ------------------------------------------------------------------------------------------------------------------
if [ $param = '-clean' ]; then
    echo $note_color rm *.bo *.ba *.cxx *.h *.o $sim_exe_file $sim_so_file $v_vcd_file_tmp $default_color
    rm *.bo *.ba *.cxx *.h *.o $sim_exe_file $sim_so_file $v_vcd_file_tmp  >/dev/null  2>/dev/null
    exit
fi


# check whether if BSV compiler exist, if not, raise error ------------------------------------------------------------------------------------------------------------------
$compiler_name -help > /dev/null             # try to run BSV compiler
if [ $? -ne 0 ]; then
    echo $error_color"Error: BSV compiler \""$compiler_name"\" not found!"$default_color
    exit
fi


# if top_file not exist, raise Error ------------------------------------------------------------------------------------------------------------------
if [ ! -f $top_file ]; then
    echo $error_color"Error: "$top_file" not found!"
    echo "make sure to run the build command in the directory which contains" $top_file $default_color
    exit
fi


# print option variables ------------------------------------------------------------------------------------------------------------------
echo "top module:" $note_color$top_module$default_color
echo "top file  :" $note_color$top_file$default_color
echo "print simulation log to:" $note_color$log_file$default_color
echo ""


# this while structure do not play a roll of loop, because it break at the end of loop.
# this just provide a way to break the while structure with a "break" statement wherever you want, so as to execute the commands after the while structure.
while : ; do
    
    if [ $param = '-bs' ] || [ $param = '-bw' ] || [ $param = '-bsw' ]; then   # use BSV as simulation engine ------------------------------------------------------------------------------------------------------------------
    
        echo "maximum simulation time argument:" $time_argument
        echo ""

        # compile BSV to objects
        echo $note_color$compiler_name -sim -g $top_module -u $top_file $default_color
        $compiler_name -sim -g $top_module -u $top_file
        if [ $? -ne 0 ]; then
            echo $error_color"Error: failed to compile!"$default_color
            break
        fi
        
        # link objects into a Bluespec simulation executable file
        echo $note_color$compiler_name -sim -e $top_module -o $sim_exe_file $default_color
        $compiler_name -sim -e $top_module -o $sim_exe_file
        if [ $? -ne 0 ]; then
            echo $error_color"Error: failed to link!"$default_color
            break
        fi
        rm *.bo *.ba *.cxx *.h *.o
        if [ ! -f $sim_exe_file ]; then
            echo $error_color"Error: failed to generate "$sim_exe_file" !"$default_color
            break
        fi
        
        # run simulation
        echo $note_color
        if [ $param = '-bw' ] || [ $param = '-bsw' ]; then
            echo ./$sim_exe_file $time_argument -V $b_vcd_file ">" $log_file $runsim_color
            ./$sim_exe_file $time_argument -V $b_vcd_file > $log_file
        else
            echo ./$sim_exe_file $time_argument ">" $log_file $runsim_color
            ./$sim_exe_file $time_argument > $log_file
        fi
        echo $default_color
        
        # check whether if wave file exist
        if [ $param = '-bw' ] || [ $param = '-bsw' ]; then
            if [ ! -f $b_vcd_file ]; then
                echo $error_color"Error: failed to generated wave file: "$b_vcd_file" !"$default_color
            else 
                echo "generated wave file: "$note_color$b_vcd_file$default_color
            fi
        fi
        
    elif [ $param = '-v' ] || [ $param = '-vs' ] || [ $param = '-vw' ] || [ $param = '-vsw' ]; then   # use Verilog as simulation engine ------------------------------------------------------------------------------------------------------------------

        # compile BSV to Verilog
        echo $note_color$compiler_name -verilog -g $top_module -u $top_file $default_color
        $compiler_name -verilog -g $top_module -u $top_file
        if [ $? -ne 0 ]; then
            echo $error_color"Error: failed to generate Verilog !"$default_color
            break
        fi
        
        # if param="-v" , only need to generate verilog source, exit now
        if [ $param = '-v' ]; then 
            break
        fi
        
        # link Verilog into a simulation executable file
        echo $note_color$compiler_name -verilog -e $top_module -o $sim_exe_file -vsim iverilog $top_module".v" $default_color
        $compiler_name -verilog -e $top_module -o $sim_exe_file -vsim iverilog $top_module".v"
        if [ $? -ne 0 ]; then
            echo $error_color"Error: failed to link Verilog source(s)!"$default_color
            break
        fi
        if [ ! -f $sim_exe_file ]; then
            echo $error_color"Error: failed to generate "$sim_exe_file" !"$default_color
            break
        fi
        
        # run simulation
        echo $note_color
        if [ $param = '-vw' ] || [ $param = '-vsw' ]; then
            echo ./$sim_exe_file +bscvcd ">" $log_file $runsim_color
            ./$sim_exe_file +bscvcd > $log_file
        else 
            echo ./$sim_exe_file ">" $log_file $runsim_color
            ./$sim_exe_file > $log_file
        fi
        echo $default_color
        
        # check whether if wave file exist
        if [ $param = '-vw' ] || [ $param = '-vsw' ]; then
            if [ ! -f $v_vcd_file_tmp ]; then
                echo $error_color"Error: failed to generated wave file!"$default_color
            else
                mv $v_vcd_file_tmp $v_vcd_file
                echo "generated wave file: "$note_color$v_vcd_file$default_color
            fi
        fi
        
    else    # --------------------------------------------------------------------------------------------------------------------------------------------
        echo $error_color"Error: invalid argument: "$param$default_color
        exit
    fi

    break
done


# previous while structure break to here
# clean anyway, remove all compile temporary files.
rm *.bo *.ba *.cxx *.h *.o $sim_exe_file $sim_so_file $v_vcd_file_tmp  >/dev/null  2>/dev/null
